.org 0x0
@ This is loaded at
@ 0x13FF0000: Fake malloc pool
_start: .global _start
@ empty

.org 0x100
@ 0x13FF0100: Fake malloc chunk and jump target
@ The second four bytes will be overwritten by a pointer,
@ several words after that have to be zero.
b start
.word 0x0
.word 0x0
.word 0x0
start:
push {r4, r5, lr}

@ On HW-W+, we need to jump into a LCD buffer mirror
@ CAS or non-CAS?
ldr r4, =0x1041F1C8 @ CASCX
cmp lr, r4
ldreq r4, =0x111516D4 @ lcd_mirror_ptr on CAS CX
ldrne r4, =0x110ED6D4 @ and on CX
ldr r4, [r4] @ Address of LCD mirror
cmp r4, #0
beq bootstrapped @ Not HW-W+
@ We need to jump into the OS's internal lcd mirror.
@ However, it has caching enabled so we need to flush it
clear_cache:
mrc p15, 0, r15, c7, c10, 3
bne clear_cache
mov r5, #0
mcr p15, 0, r5, c7, c7, 0
add r4, r4, #(bootstrapped-_start) @ Calculate address
add r4, r4, #(0x13ff0000-0x13fda800) @ of bootstrapped in mirror
mov pc, r4 @ and jump to it
.ltorg

bootstrapped:
@ CAS or non-CAS?
ldr r4, =0x1041F1C8 @ CASCX
cmp lr, r4
beq cascx
ldr r4, =0x111BF4A4 @ pool ptr
ldr r5, =0x1041EAB8 @ free
b find

cascx:
ldr r4, =0x112234AC @ pool ptr
ldr r5, =0x1041F038 @ free

find:
@ Find broken chunk
ldr r0, [r4, #0x34] @ pool->first chunk
ldr r1, =0x13FF0000 @ fake_pool

loop:
ldr r2, [r0, #0x0] @ r2 = r0->next
ldr r3, [r2, #0xc] @ r3 = chunk->next->pool
cmp r3, r1 @ chunk->next->pool == fake_pool?
beq found
mov r0, r2 @ Nope -> next
b loop
found: @ Yep -> r2 is overflown/broken chunk

@ Repair it
add r1, r2, #0xA00000 @ sizeof(formatManager_table) + 0x100 ...
sub r1, r1, #0x100 @ ... - 0x100
add r1, r1, #0x10 @ sizeof(malloc_header)
str r1, [r2, #0x0] @ broken->next = broken + sizeof(malloc_header) + sizeof(formatManager_table)
str r0, [r2, #0x4] @ broken->prev = r0
mov r1, #0
str r1, [r2, #0x8] @ broken->free = 0
str r4, [r2, #0xc] @ broken->pool = pool
add r0, r2, #0x10
blx r5 @ free(broken + sizeof(malloc_header))

bl call_main

mov r0, #0
pop {r4, r5, pc}

call_main:
	stmfd sp!, {r4-r11,lr} @ and never destroy r0 and r1 which are C's argc and argv
	@ GOT-based relocation, required for C global variables. The program must be built with the GCC option -fpic.
relocate:
	@ Get the absolute address of the GOT. See http://www.google.com/codesearch/p?hl=en#FiIujMxKUHU/sites/sources.redhat.com/pub/glibc/snapshots/glibc-ports-latest.tar.bz2%7CDNu48aiJSpY/glibc-ports-20090518/sysdeps/arm/dl-machine.h&q=%22.word%20_GLOBAL_OFFSET_TABLE_%22
	ldr   r2, got_offset
get_got_offset:	
	add   r2, pc, r2
	adr   r3, _start
	ldr   r5, =__got_size
relocate_loop:
	subs  r5, #1
	ldrge r4, [r2]       @ next GOT entry
	addge r4, r4, r3     @ calculate the absolute address
	strge r4, [r2], #4   @ store it back to the GOT
	bge   relocate_loop

	str  sp, __crt0_savedsp
	bl   main
__crt0exit: .global __crt0exit
	ldmfd sp!, {r4-r11,pc}

got_offset:
	.word _GLOBAL_OFFSET_TABLE_ - (get_got_offset+8)
__crt0_savedsp: .global __crt0_savedsp
	.long 0
